<script lang="tsx">
import type { CSSProperties, FunctionalComponent } from 'vue'
import { computed, defineComponent, nextTick, onMounted, onUnmounted, ref, unref } from 'vue'
import { Divider, Menu } from 'ant-design-vue'
import type { Axis, ContextMenuItem, ItemContentProps } from './typing'
import { Icon } from '@/components/Icon'

const prefixCls = 'context-menu'

const props = {
  width: { type: Number, default: 156 },
  customEvent: { type: Object as PropType<Event>, default: null },
  styles: { type: Object as PropType<CSSProperties> },
  showIcon: { type: Boolean, default: true },
  axis: {
    // The position of the right mouse button click
    type: Object as PropType<Axis>,
    default() {
      return { x: 0, y: 0 }
    },
  },
  items: {
    // The most important list, if not, will not be displayed
    type: Array as PropType<ContextMenuItem[]>,
    default() {
      return []
    },
  },
}

const ItemContent: FunctionalComponent<ItemContentProps> = (props) => {
  const { item } = props
  return (
    <span class="inline-block w-full px-4" onClick={props.handler.bind(null, item)}>
      {props.showIcon && item.icon && <Icon class="mr-2" icon={item.icon} />}
      <span>{item.label}</span>
    </span>
  )
}

export default defineComponent({
  name: 'ContextMenu',
  props,
  setup(props) {
    const wrapRef = ref(null)
    const showRef = ref(false)

    const getStyle = computed((): CSSProperties => {
      const { axis, items, styles, width } = props
      const { x, y } = axis || { x: 0, y: 0 }
      const menuHeight = (items || []).length * 40
      const menuWidth = width
      const body = document.body

      const left = body.clientWidth < x + menuWidth ? x - menuWidth : x
      const top = body.clientHeight < y + menuHeight ? y - menuHeight : y
      return {
        position: 'absolute',
        width: `${width}px`,
        left: `${left + 1}px`,
        top: `${top + 1}px`,
        zIndex: 9999,
        ...styles,
      }
    })

    onMounted(() => {
      nextTick(() => (showRef.value = true))
    })

    onUnmounted(() => {
      const el = unref(wrapRef)
      el && document.body.removeChild(el)
    })

    function handleAction(item: ContextMenuItem, e: MouseEvent) {
      const { handler, disabled } = item
      if (disabled)
        return

      showRef.value = false
      e?.stopPropagation()
      e?.preventDefault()
      handler?.()
    }

    function renderMenuItem(items: ContextMenuItem[]) {
      const openItems = items.filter(item => !item.hidden)
      return openItems.map((item) => {
        const { disabled, label, children, divider = false } = item

        const contentProps = {
          item,
          handler: handleAction,
          showIcon: props.showIcon,
        }

        if (!children || children.length === 0) {
          return (
            <>
              <Menu.Item disabled={disabled} class={`${prefixCls}__item`} key={label}>
                <ItemContent {...contentProps} />
              </Menu.Item>
              {divider ? <Divider key={`d-${label}`} /> : null}
            </>
          )
        }
        if (!unref(showRef))
          return null

        return (
          <Menu.SubMenu key={label} disabled={disabled} popupClassName={`${prefixCls}__popup`}>
            {{
              title: () => <ItemContent {...contentProps} />,
              default: () => renderMenuItem(children),
            }}
          </Menu.SubMenu>
        )
      })
    }
    return () => {
      if (!unref(showRef))
        return null

      const { items } = props
      return (
        <Menu inlineIndent={12} mode="vertical" class={prefixCls} ref={wrapRef} style={unref(getStyle)}>
          {renderMenuItem(items)}
        </Menu>
      )
    }
  },
})
</script>

<style lang="less">
@default-height: 42px !important;

@small-height: 36px !important;

@large-height: 36px !important;

.item-style() {
  li {
    display: inline-block;
    width: 100% !important;
    height: @default-height;
    margin: 0 !important;
    line-height: @default-height;

    span {
      line-height: @default-height;
    }

    >div {
      width: 100% !important;
      height: 100% !important;
      margin: 0 !important;
    }

    &:not(.ant-menu-item-disabled):hover {
      // color: @text-color;
      background-color: @item-hover-bg;
    }
  }
}

.context-menu {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 200;
  display: block;
  width: 156px;
  margin: 0;
  list-style: none;
  user-select: none;
  background-color: var(--component-background);
  background-clip: padding-box;
  border: 1px solid rgb(0 0 0 / 8%);
  border-radius: 8px;
  box-shadow:
    0 2px 2px 0 rgb(0 0 0 / 14%),
    0 3px 1px -2px rgb(0 0 0 / 10%),
    0 1px 5px 0 rgb(0 0 0 / 6%);

  &__item {
    margin: 0 !important;
  }

  .item-style();

  .ant-divider {
    margin: 0;
  }

  &__popup {
    .ant-divider {
      margin: 0;
    }

    .item-style();
  }

  .ant-menu-submenu-title,
  .ant-menu-item {
    padding: 0 !important;
  }
}
</style>
